// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/child/indexed_db/indexed_db_dispatcher.h"

#include <utility>

#include "base/lazy_instance.h"
#include "base/memory/ptr_util.h"
#include "base/threading/thread_local.h"
#include "content/child/indexed_db/indexed_db_key_builders.h"
#include "content/child/indexed_db/webidbcursor_impl.h"
#include "ipc/ipc_channel.h"
#include "third_party/WebKit/public/platform/modules/indexeddb/WebIDBDatabaseCallbacks.h"
#include "third_party/WebKit/public/platform/modules/indexeddb/WebIDBObservation.h"

using base::ThreadLocalPointer;
using blink::WebIDBKey;
using blink::WebIDBObservation;

namespace content {
static base::LazyInstance<ThreadLocalPointer<IndexedDBDispatcher>>::Leaky
    g_idb_dispatcher_tls
    = LAZY_INSTANCE_INITIALIZER;

namespace {

    IndexedDBDispatcher* const kHasBeenDeleted = reinterpret_cast<IndexedDBDispatcher*>(0x1);

} // unnamed namespace

IndexedDBDispatcher::IndexedDBDispatcher()
{
    g_idb_dispatcher_tls.Pointer()->Set(this);
}

IndexedDBDispatcher::~IndexedDBDispatcher()
{
    in_destructor_ = true;
    mojo_owned_callback_state_.clear();
    mojo_owned_database_callback_state_.clear();

    g_idb_dispatcher_tls.Pointer()->Set(kHasBeenDeleted);
}

IndexedDBDispatcher* IndexedDBDispatcher::ThreadSpecificInstance()
{
    if (g_idb_dispatcher_tls.Pointer()->Get() == kHasBeenDeleted) {
        NOTREACHED() << "Re-instantiating TLS IndexedDBDispatcher.";
        g_idb_dispatcher_tls.Pointer()->Set(NULL);
    }
    if (g_idb_dispatcher_tls.Pointer()->Get())
        return g_idb_dispatcher_tls.Pointer()->Get();

    IndexedDBDispatcher* dispatcher = new IndexedDBDispatcher();
    if (WorkerThread::GetCurrentId())
        WorkerThread::AddObserver(dispatcher);
    return dispatcher;
}

void IndexedDBDispatcher::WillStopCurrentWorkerThread()
{
    delete this;
}

void IndexedDBDispatcher::RegisterMojoOwnedCallbacks(
    IndexedDBCallbacksImpl::InternalState* callbacks)
{
    mojo_owned_callback_state_[callbacks] = base::WrapUnique(callbacks);
}

void IndexedDBDispatcher::UnregisterMojoOwnedCallbacks(
    IndexedDBCallbacksImpl::InternalState* callbacks)
{
    if (in_destructor_)
        return;

    auto it = mojo_owned_callback_state_.find(callbacks);
    DCHECK(it != mojo_owned_callback_state_.end());
    it->second.release();
    mojo_owned_callback_state_.erase(it);
}

void IndexedDBDispatcher::RegisterMojoOwnedDatabaseCallbacks(
    blink::WebIDBDatabaseCallbacks* callbacks)
{
    mojo_owned_database_callback_state_[callbacks] = base::WrapUnique(callbacks);
}

void IndexedDBDispatcher::UnregisterMojoOwnedDatabaseCallbacks(
    blink::WebIDBDatabaseCallbacks* callbacks)
{
    if (in_destructor_)
        return;

    auto it = mojo_owned_database_callback_state_.find(callbacks);
    DCHECK(it != mojo_owned_database_callback_state_.end());
    it->second.release();
    mojo_owned_database_callback_state_.erase(it);
}

void IndexedDBDispatcher::RegisterCursor(WebIDBCursorImpl* cursor)
{
    DCHECK(!base::ContainsValue(cursors_, cursor));
    cursors_.insert(cursor);
}

void IndexedDBDispatcher::UnregisterCursor(WebIDBCursorImpl* cursor)
{
    DCHECK(base::ContainsValue(cursors_, cursor));
    cursors_.erase(cursor);
}

void IndexedDBDispatcher::ResetCursorPrefetchCaches(
    int64_t transaction_id,
    WebIDBCursorImpl* exception_cursor)
{
    for (WebIDBCursorImpl* cursor : cursors_) {
        if (cursor != exception_cursor && cursor->transaction_id() == transaction_id)
            cursor->ResetPrefetchCache();
    }
}

} // namespace content
